/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <openssl/md5.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>

#include "SMAEncoder.h"
#include "SMADigester.h"

CFDataRef SMACreateMD5DigestFromData(CFAllocatorRef allocator, CFDataRef data)
{
   if (data == NULL) {
      return NULL;
   }
   
   unsigned char hash[MD5_DIGEST_LENGTH];
   MD5((const unsigned char*) CFDataGetBytePtr(data), CFDataGetLength(data), hash);
   
   return CFDataCreate(allocator, hash, MD5_DIGEST_LENGTH);   
}

CFDataRef SMACreateMD5DigestFromBytes(CFAllocatorRef allocator, const UInt8* data, CFIndex length)
{
   if (data == NULL) {
      return NULL;
   }
   
   unsigned char hash[MD5_DIGEST_LENGTH];
   MD5((const unsigned char*) data, length, hash);
   
   return CFDataCreate(allocator, hash, MD5_DIGEST_LENGTH);   
}

CFDataRef SMACreateMD5DigestFromString(CFAllocatorRef allocator, CFStringRef s, CFStringEncoding encoding)
{
   if (s == NULL) {
      return NULL;
   }
   
   CFDataRef data = CFStringCreateExternalRepresentation(allocator, s, encoding, 0x00);
   if (data == NULL) {
      return NULL;
   }

   CFDataRef digest = SMACreateMD5DigestFromData(allocator, data);
   
   CFRelease(data);

   return digest;
}

CFStringRef SMACreateHexMD5DigestFromData(CFAllocatorRef allocator, CFDataRef data)
{
   if (data == NULL) {
      return NULL;
   }
   
   unsigned char hash[MD5_DIGEST_LENGTH];
   MD5((const unsigned char*) CFDataGetBytePtr(data), CFDataGetLength(data), hash);
   
   return SMAEncodeHexFromBytes(allocator, (const UInt8*) hash, MD5_DIGEST_LENGTH);
}

CFStringRef SMACreateHexMD5DigestFromBytes(CFAllocatorRef allocator, const UInt8* data, CFIndex length)
{
   if (data == NULL) {
      return NULL;
   }
   
   unsigned char hash[MD5_DIGEST_LENGTH];
   MD5((const unsigned char*) data, length, hash);
   
   return SMAEncodeHexFromBytes(allocator, (const UInt8*) hash, MD5_DIGEST_LENGTH);
}

CFStringRef SMACreateHexMD5DigestFromString(CFAllocatorRef allocator, CFStringRef s, CFStringEncoding encoding)
{
   CFStringRef digest = NULL;

   if (s == NULL) {
      return NULL;
   }
   
   CFDataRef data = CFStringCreateExternalRepresentation(allocator, s, encoding, 0x00);
   if (data != NULL) {
      unsigned char hash[MD5_DIGEST_LENGTH];
      MD5((const unsigned char*) CFDataGetBytePtr(data), CFDataGetLength(data), hash);
      digest = SMAEncodeHexFromBytes(allocator, (const UInt8*) hash, MD5_DIGEST_LENGTH);
      CFRelease(data);
   }

   return digest;
}

//

CFDataRef SMACreateSHA1HMACDigestFromData(CFAllocatorRef allocator, CFDataRef data, CFDataRef key)
{
   if (data == NULL || key == NULL) {
      return NULL;
   }
   
   unsigned char hash[EVP_MAX_MD_SIZE];
   HMAC(EVP_sha1(), CFDataGetBytePtr(key), CFDataGetLength(key), CFDataGetBytePtr(data), CFDataGetLength(data), hash, NULL);
   
   return CFDataCreate(allocator, hash, SHA_DIGEST_LENGTH);
}

CFDataRef SMACreateSHA1HMACDigestFromString(CFAllocatorRef allocator, CFStringRef s, CFStringEncoding encoding, CFDataRef key)
{
   if (s == NULL || s == NULL || key == NULL) {
      return NULL;
   }
   
   CFDataRef data = CFStringCreateExternalRepresentation(allocator, s, encoding, 0x00);
   if (data == NULL) {
      return NULL;
   }
   
   CFDataRef digest = SMACreateSHA1HMACDigestFromData(allocator, data, key);
   
   CFRelease(data);
   
   return digest;
}

#if defined(SMADIGESTER_TEST)
int main()
{
   CFDataRef data = CFDataCreate(NULL, (const UInt8*) "q1w2e3", 6);

   {
      CFDataRef digest = SMACreateMD5DigestFromData(NULL, data);
      CFShow(digest);
      CFRelease(digest);
   }

   {
      CFDataRef digest = SMACreateMD5DigestFromString(NULL, CFSTR("q1w2e3"), kCFStringEncodingASCII);
      CFShow(digest);
      CFRelease(digest);      
   }
   
   return 0;
}
#endif
